%%%-------------------------------------------------------------------
%%% @author GAME4
%%% @copyright (C) 2018, <COMPANY>
%%% @doc
%%% hprose 序列化
%%% @end
%%% Created : 11. 五月 2018 11:22
%%%-------------------------------------------------------------------
-module(hprose_writer).
-author("GAME4").
-include("hprose.hrl").

%% API
-export([serialize/1]).

serialize(V) ->
  serialize(V, []).

serialize([Value | T], Stream) when is_integer(Value), Value >= 0, Value =< 9 ->
  serialize(T, [integer_to_binary(Value) | Stream]);

serialize([Value | T], Stream) when is_integer(Value), Value > 16#FFFFFFFF ->
  serialize(T, [?TAG_LONG, integer_to_binary(Value) | Stream]);

serialize([Value | T], Stream) when is_integer(Value) ->
  serialize(T, [?TAG_INTEGER, integer_to_binary(Value) | Stream]);

serialize([Value | T], Stream) when is_float(Value), Value =< 1.7976931348623157e+308, Value >= 2.2250738585072014E-308 ->
  serialize(T, [?TAG_DOUBLE, float_to_binary(Value) | Stream]);

serialize([Value | T], Stream) when is_float(Value), Value > 1.7976931348623157e+308 ->
  serialize(T, [?TAG_INFINITY, ?TAG_POS | Stream]);

serialize([Value | T], Stream) when is_float(Value), Value < 2.2250738585072014E-308 ->
  serialize(T, [?TAG_INFINITY, ?TAG_NEG | Stream]);

serialize([true | T], Stream) ->
  serialize(T, [?TAG_TRUE | Stream]);

serialize([false | T], Stream) ->
  serialize(T, [?TAG_FALSE | Stream]);

serialize([{char, Char} | T], Stream) when Char < 16#110000 ->
  serialize(T, [?TAG_UTF8CHAR, Char | Stream]);

serialize([null | T], Stream) ->
  serialize(T, [?TAG_NULL | Stream]);

serialize([empty | T], Stream) ->
  serialize(T, [?TAG_EMPTY | Stream]);

serialize([{{Year, Month, Day}, {0, 0, 0, 0}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0 ->
  serialize(T, [?TAG_DATE, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), ?TAG_SEMICOLON | Stream]);

serialize([{utc, {Year, Month, Day}, {0, 0, 0, 0}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0 ->
  serialize(T, [?TAG_DATE, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), ?TAG_UTC | Stream]);

serialize([{{0, 0, 0}, {Hour, Minute, Seconds, 0}} | T], Stream) when Hour =/= 0, Minute =/= 0, Seconds =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_SEMICOLON | Stream]);

serialize([{utc, {0, 0, 0}, {Hour, Minute, Seconds, 0}} | T], Stream) when Hour =/= 0, Minute =/= 0, Seconds =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_UTC | Stream]);

serialize([{{0, 0, 0}, {Hour, Minute, Seconds, Millisecond}} | T], Stream) when Hour =/= 0, Minute =/= 0, Seconds =/= 0, Millisecond =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_POINT, integer_to_binary(Millisecond), ?TAG_SEMICOLON | Stream]);

serialize([{utc, {0, 0, 0}, {Hour, Minute, Seconds, Millisecond}} | T], Stream) when Hour =/= 0, Minute =/= 0, Seconds =/= 0, Millisecond =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_POINT, integer_to_binary(Millisecond), ?TAG_UTC | Stream]);

serialize([{{Year, Month, Day}, {Hour, Minute, Seconds, 0}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0, Hour =/= 0, Minute =/= 0, Seconds =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_SEMICOLON | Stream]);

serialize([{utc, {Year, Month, Day}, {Hour, Minute, Seconds, 0}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0, Hour =/= 0, Minute =/= 0, Seconds =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_UTC | Stream]);

serialize([{{Year, Month, Day}, {Hour, Minute, Seconds, Millisecond}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0, Hour =/= 0, Minute =/= 0, Seconds =/= 0, Millisecond =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_POINT, integer_to_binary(Millisecond), ?TAG_SEMICOLON | Stream]);

serialize([{utc, {Year, Month, Day}, {Hour, Minute, Seconds, Millisecond}} | T], Stream) when Year =/= 0, Month =/= 0, Day =/= 0, Hour =/= 0, Minute =/= 0, Seconds =/= 0, Millisecond =/= 0 ->
  serialize(T, [?TAG_TIME, integer_to_binary(Year), integer_to_binary(Month), integer_to_binary(Day), integer_to_binary(Hour), integer_to_binary(Minute), integer_to_binary(Seconds), ?TAG_POINT, integer_to_binary(Millisecond), ?TAG_UTC | Stream]);

serialize([<<>> | T], Stream) ->
  serialize(T, [?TAG_BYTES, ?TAG_QUOTE, ?TAG_QUOTE | Stream]);

serialize([<<Binary/binary>> | T], Stream) ->
  serialize(T, [?TAG_BYTES, byte_size(Binary), ?TAG_QUOTE, Binary, ?TAG_QUOTE | Stream]);

serialize([{char, Char} | T], Stream) ->
  serialize(T, [?TAG_UTF8CHAR, Char | Stream]);

serialize([{string, []} | T], Stream) ->
  serialize(T, [?TAG_STRING, ?TAG_QUOTE, ?TAG_QUOTE | Stream]);

serialize([{string, String} | T], Stream) when is_list(String) ->
  serialize(T, [?TAG_STRING, length(String), ?TAG_QUOTE, String, ?TAG_QUOTE | Stream]);

serialize([{guid, Guid} | T], Stream) ->
  serialize(T, [?TAG_GUID, ?TAG_OPENBRACE, Guid, ?TAG_CLOSEBRACE | Stream]);

serialize([[] | T], Stream) ->
  serialize(T, [?TAG_LIST, ?TAG_OPENBRACE, ?TAG_CLOSEBRACE | Stream]);

serialize([List | T], Stream) when is_list(List) ->
  serialize(T, [?TAG_LIST, length(List), ?TAG_OPENBRACE] ++ serialize(List) ++ [?TAG_CLOSEBRACE | Stream]);

serialize([#{} | T], Stream) ->
  serialize(T, [?TAG_MAP, ?TAG_OPENBRACE, ?TAG_CLOSEBRACE | Stream]);

serialize([Map | T], Stream) when is_map(Map) ->
  serialize(T, [?TAG_MAP, map_size(Map), ?TAG_OPENBRACE] ++ serialize(Map) ++ [?TAG_CLOSEBRACE | Stream]);

serialize([], Stream) -> lists:reverse(Stream).



